/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package semant.second_pass;

import util.List;
import semant.Env;
import symbol.ClassInfo;
import symbol.MethodInfo;
import symbol.Symbol;
import symbol.VarInfo;
import syntaxtree.IdentifierType;
import syntaxtree.MethodDecl;
import syntaxtree.Type;
import syntaxtree.VisitorAdapter;

/**
 *
 * @author Thiago
 */
class MethodDeclHandler extends VisitorAdapter{


    Env env;

    ClassInfo info;

    MethodDeclHandler(Env e, ClassInfo c)
    {
        env = e;

        info = c;
    }

    static void secondPass(Env e, ClassInfo c, MethodDecl node) {
        MethodDeclHandler h = new MethodDeclHandler(e, c);

        node.accept(h);
    }

    @Override
    public void visit(MethodDecl node)

    {

        Symbol name = Symbol.symbol(node.name.s);
        MethodInfo m = info.methods.get(Symbol.symbol(node.name.s));

        StatementListHandler.secondPass(env, info, m, node.body);

        Type retorno = ExpHandler.secondPass(env, info, m, node.returnExp);

        if (!Type.compatible(env,node.returnType,retorno))
        {
            env.err.Error(node.returnExp, new Object[]{
                        "Expressão retornada de tipo invalido",
                        "Esperado: " + node.returnType,
                        "Encontrado: " + retorno});
        }
        ClassInfo c = info.base;
        while(c!= null){
            MethodInfo old = c.methods.get(name);
            if(old!=null){
                if(!Type.equal(old.type,m.type)){
                    env.err.Error(node.name, new Object[]{"Alteracao no tipo de retorno do metodo \'"+ name + "\' na  classe \'" + info.name + "\'",
                                        "Esperado: "+old.type.toString(),
                                        "Encontrado: " + m.type.toString(),
                                        "Metodo herdado da classe " + c.name});
                }
                if(old.formals != null && m.formals!=null){
                    if(old.formals.size() !=  m.formals.size()){
                        env.err.Error(node.name, new Object[]{"Alteracao no numero de parametros do metodo \'"+ name + "\' na  classe \'" + info.name + "\'",
                                     "Esperado: "+old.formals.size(),
                                     "Encontrado: " + m.formals.size(),
                                     "Metodo herdado da classe " + c.name});
                    }
                    else{
                        int i = 0;
                        List<VarInfo> para = old.formals;
                        for(List<VarInfo> atual = m.formals; atual!= null ; atual = atual.tail)
                        {
                            i++;
                            if(atual.head.type.getClass() != para.head.type.getClass())
                            {
                                env.err.Error(node.name, new Object[]{
                                    "Alteracao no "+ String.valueOf(i) + "º parametro do metodo "+node.name.s+
                                            " na classe "+ info.name,
                                            "Esperado: "+para.head.type.toString(),
                                            "Encontrado: " + atual.head.type.toString(),
                                            "Metodo herdade da classe "+ c.name});
                            }
                            else{
                                if(atual.head.type instanceof IdentifierType)
                                {
                                    ClassInfo dest = env.classes.get(Symbol.symbol(((IdentifierType)atual.head.type).name));
                                    ClassInfo source = env.classes.get(Symbol.symbol(((IdentifierType)para.head.type).name));
                                    if(!((IdentifierType)atual.head.type).name.equalsIgnoreCase(((IdentifierType)para.head.type).name))
                                    {
                                    env.err.Error(node.name, new Object[]{
                                    "Alteracao no "+ String.valueOf(i) + "º parametro do metodo "+node.name.s+
                                            " na classe "+ info.name,
                                            "Esperado: "+source.name,
                                            "Encontrado: " + dest.name,
                                            "Metodo herdade da classe "+ c.name});
                                    }
                                }
                            }


                            para = para.tail;
                        }
                    }

                }
                else if((old.formals != null && m.formals==null)||(old.formals == null && m.formals!=null) ){
                    int size1=0, size2=0;
                    if(m.formals==null){
                        size1=0;
                        size2=old.formals.size();
                    }

                    if(old.formals==null){
                        size1=m.formals.size();
                        size2=0;
                    }
                    env.err.Error(node.name, new Object[]{"AlteraÃ§Ã£o no nÃºmero de parÃ¢metros do mÃ©todo \'"+ name + "\' na  classe \'" + info.name + "\'",
                                        "Esperado: "+String.valueOf(size1),
                                        "Encontrado: " + String.valueOf(size2),
                                        "MÃ©todo herdado da classe " + c.name});

                }
            }
            c = c.base;
        }
    }

}
